package parser;

import java.util.Iterator;
import java.util.List;

import net.htmlparser.jericho.Attribute;
import net.htmlparser.jericho.Attributes;
import net.htmlparser.jericho.Element;
import net.htmlparser.jericho.HTMLElementName;
import net.htmlparser.jericho.Source;
import sadxmlreader.TemplateStructure;

public class Sad {
	

	private static final String SLASH = "/";
	private static final String ROOT = "root";
	private String title;
	private boolean tittleSet= false;
	private List<TemplateStructure> templateStructure;
	private Source wikiSource;
	private int lastIndexValid=0;
	private UrlWrapper structureWrapped;
	private boolean sadGenerated= false;
	
	/**
	 * @return the sadGenerated
	 */
	public boolean isSadGenerated() {
		return sadGenerated;
	}

	/**
	 * @param sadGenerated the sadGenerated to set
	 */
	public void setSadGenerated(boolean sadGenerated) {
		this.sadGenerated = sadGenerated;
	}

	public Sad(String urlBase, List<TemplateStructure> templateFormat, Source source) {
		this.urlBase= urlBase;
		this.templateStructure=templateFormat;
		this.wikiSource = source;
		
	}

	/**
	 * @return the urlBase
	 */
	public String getUrlBase() {
		return urlBase;
	}

	/**
	 * @param urlBase the urlBase to set
	 */
	public void setUrlBase(String urlBase) {
		this.urlBase = urlBase;
	}

	private String urlBase;


	
	/**
	 * @return the title
	 */
	public String getTitle() {
		return title;
	}

	/**
	 * @param title the title to set
	 */
	public void setTitle(String title) {
		this.title = title;
		this.tittleSet= true;
	}

	/**
	 * @return the tittleSet
	 */
	public boolean isTittleSet() {
		return tittleSet;
	}

	
	public void generate(){
		
		

		List<TemplateStructure> template =this.templateStructure;
		List<Attribute> urls= wikiSource.getURIAttributes();
		
		validateUrls(template,urls);		
		
	}

	
	

	private void validateUrls(List<TemplateStructure> template, List<Attribute> urls) {
		
		TemplateStructure firstItem= template.get(0);
		descartarUrls(firstItem,urls);
		int urlIndex=0;
		int templateIndex=0;
		UrlWrapper structure = new UrlWrapper(getUrlBase());
		structure.setText(new StringBuilder().append(ROOT));
		boolean valid= urls.size()> 0 && validateAgainstTemplate(structure,template,templateIndex,urls,urlIndex);
		
		if (valid){
			System.out.println("Estructura VAlida");
			structure.setValid();
			descartarUrlsSobrantes(urls,this.lastIndexValid);
			this.structureWrapped=structure;
			setSadGenerated(true);
			urls.toString();
		}
		else
			{
			System.out.println("Estructura incorrecta");	
		}
	}

	private void descartarUrlsSobrantes(List<Attribute> urls, int index) {
		
		while(index < urls.size()-1){
			urls.remove(index);
				
		}		
		
		
	}

	private boolean validateAgainstTemplate(UrlWrapper structure, List<TemplateStructure> template, int templateIndex, List<Attribute> urls, int urlIndex) {
		
		boolean valid=false;
		UrlWrapper wrapper = new UrlWrapper(structure.getUrlBase());
		
		//Si nodo en el template es un item
		TemplateStructure templateNode= template.get(templateIndex);
		if(templateNode.isItem()){
			structure.addChild(wrapper);
			wrapper.setAtt(urls.get(urlIndex));
			valid = validateItem(structure,template, templateIndex, urls, urlIndex);
			
		}else{
			//Seccion con link
			if(!templateNode.isItem() && templateNode.hasLink()){
				structure.addChild(wrapper);
				wrapper.setAtt(urls.get(urlIndex));				
				valid = validateSectionWithLink(structure,template, templateIndex, urls, urlIndex);				
				
			}else{				
				//Seccion sin link
				if(!templateNode.hasLink()){
					valid = validateSectionWithoutLink(structure,template,templateIndex, urls, urlIndex);				
				  }
			}			
			
		}		
		return valid;
		
		
	}

	private boolean validateSectionWithoutLink(UrlWrapper wrapper, List<TemplateStructure> template, int templateIndex, List<Attribute> urls, int urlIndex) {
		
		boolean valid=false;
		//No hay con que hacer matching por eso no se llama a dicha funcion
		
		//Si seccion vacia recorro hasta el prox matching
		if(template.get(templateIndex).isEmpty()){				
			/**
			 * Agrego urls hast ael proximo matching... 
			 */		
			valid = validateEmptySection(wrapper,template, templateIndex , urls,urlIndex);
							
		}else{//Sin link y con hijos. se llama recursivamente solo avanzando en el template porq como no tiene link no hay qu eavanzar en la pagina
			
			valid= validateAgainstTemplate(wrapper,template,++templateIndex,urls, urlIndex); 
			
		  }
		return valid;
	}

	private boolean validateSectionWithLink(UrlWrapper wrapper, List<TemplateStructure> template, int templateIndex, List<Attribute> urls,	int urlIndex) {
		
		boolean valid=false;
		TemplateStructure templateNode = template.get(templateIndex);
		if(matching(templateNode, urls.get(urlIndex))){
			
			//Seccion VAcia
			if(templateNode.isEmpty()){
				/**
				 * Agrego urls hast ael proximo matching... 
				 */
				valid = validateEmptySection(wrapper,template,templateIndex, urls,urlIndex);
				
			}else{
				//no puedo preguntar por el siguiente del templat y de las url.. porqel template tambien es lineal. Ver d eusar las dos estructuras mezcladas.
			
				if(templateIndex < template.size()-1 && urlIndex < urls.size()-1){
					//debo validar hijos y luego avanzar. COmo la estructura del template es lineal. Simplemente llamo recursivamente avanzando en las dos estructuras
					
					valid= validateAgainstTemplate(wrapper,template,++templateIndex, urls, ++urlIndex); 					 
										
				
				}else{
					
					if(templateIndex < template.size()-1){						
						valid= true;
						this.lastIndexValid=urlIndex;
					}else{
						//Recorri las urls completas. Estructura invalida
						valid=false;
					 }				
					
				  }
			  }	
			
		}else{
			valid= false;
		}
		return valid;
	}

	private boolean validateItem(UrlWrapper wrapper, List<TemplateStructure> template, int templateIndex, List<Attribute> urls, int urlIndex) {
		boolean ret;
		
		TemplateStructure templateNode = template.get(templateIndex);
		if(matching(templateNode, urls.get(urlIndex))){
			
			//Avanzo en ambas estructuras *1			
			if(templateIndex < template.size()-1 &&  urlIndex < urls.size()-1){
				ret= validateAgainstTemplate(wrapper,template,++templateIndex,urls,++urlIndex);
			}else{
				//Recorri el template entero. Estructura valida
				if(templateIndex == template.size()-1){						
					ret= true;
					this.lastIndexValid=urlIndex;
				}else{
					//Recorri las urls completas. Estructura invalida
					ret=false;
				}				
			}			
		}
		else{
			//Corto. estructura invalida
			ret= false;
		}
		return ret;
	}

	private boolean validateEmptySection(UrlWrapper wrapper, List<TemplateStructure> template,int templateIndex, List<Attribute> urls, int urlIndex) {
		
		boolean ret=false;
		
		if(templateIndex < template.size()-1){
			
			templateIndex = getNextItemToMatch(template,templateIndex);
			TemplateStructure templateItemNext= template.get(templateIndex);
			 urlIndex=addUrlUntilNextMatch(wrapper,templateItemNext,urls,urlIndex);
			 ret= validateAgainstTemplate(wrapper,template, templateIndex, urls, urlIndex); 		
			 					
		}else{
			
			//Si entra aca se deben validar el resto de las urls. Ver la forma de solucionar porque va agregar todas las que siguen a continuacion
			urlIndex= addRemainingUrls(wrapper,urls,urlIndex);
			ret= true;
			this.lastIndexValid=urlIndex;
			
		}
		return ret;
	}

	/**
	 * Retorna el proximo elemento con el cual hacer matching. Avanza hasta que el nodo del template tenga un link
	 * @param templateIter
	 * @return
	 */
	
	private int getNextItemToMatch(List<TemplateStructure> template, int index) {
		
		TemplateStructure ret= template.get(index);
		while(!ret.hasLink()){
			if(index < template.size())
			  ret= template.get(++index);
			}
		
		return index;
	}

	/**
	 * Avanza hasta el final del iterador
	 * @param wrapper 
	 * @param urlsIter
	 */
	private int addRemainingUrls(UrlWrapper wrapper, List<Attribute> urls, int urlIndex) {

		
		while(urlIndex < urls.size()){		
			UrlWrapper child= new UrlWrapper(urls.get(urlIndex));
			wrapper.addChild(child);
			urlIndex++;
		}
		
		
		
		return urlIndex;
		
		
	}

	/**
	 * Avanza en el iterador mientra no haga matching. 
	 * @param wrapper 
	 * @param templateItemNext
	 * @param urlsIter
	 */
	private int addUrlUntilNextMatch(UrlWrapper wrapper, TemplateStructure templateItemNext, List<Attribute> urls, int urlIndex) {
		
		
		//Mientras haya urls y no haga matching sigue avanzando ene el iterador
		while(urlIndex < urls.size() && !matching(templateItemNext, urls.get(urlIndex))){		
			UrlWrapper child= new UrlWrapper(urls.get(urlIndex));
			wrapper.addChild(child);
			urlIndex++;
		}
		return urlIndex;
		
	}

	
	private void descartarUrls(TemplateStructure templateItem,
			List<Attribute> urls) {
		boolean matching = false;
		int indexFirstMatch=0;		
		for(;!matching && indexFirstMatch < urls.size(); indexFirstMatch++){
			if(matching(templateItem,urls.get(indexFirstMatch))){				
				matching= true;				
			}else{
				urls.remove(indexFirstMatch);
				indexFirstMatch--;
			}			
		}
		
	}

	private boolean matching(TemplateStructure templateItem, Attribute url) {
		
			
		//Reemplazo los espacios en blanco en los titulos del template por guines bajpos para pder comparar con la url en la wiki
		String title = templateItem.getTitle().replaceAll(" ", "_");
		//Obtengo la url del tag actual
		String rawUrl=url.getValue().substring(1, url.getValue().length());
		//Me quedo con el final despues de la ultima barra
		String[] linkSplit = rawUrl.split(SLASH); 
		String link = linkSplit[linkSplit.length-1];
		
		/**
		 * A veces el link en la url tiene agregado adelante el nombre del proyecto o algun otro valor
		 * entonces busco las diferentes palabras del titulo del template en la url
		 */
		
//		String[] titlesPotentials= templateItem.getTitle().split(" ");
//		String titlePotential="";
//		
//		for (int i = 0; i < titlesPotentials.length; i++) {
//			titlePotential= titlesPotentials[i];
//			if(link.contains(titlePotential)){
//				return true;
//			}
//				
//			
//		}	
		
		return  link.toLowerCase().contains(title.toLowerCase());
	}

	



	


	


private String getAttValue(Element elemento,
		String key) {
	
	String rta="";
	Attributes att= elemento.getAttributes();
	boolean exist=false;
	 if(att!=null){
		 
	 Iterator<Attribute> atributos = att.iterator();
	 while (atributos.hasNext() && !exist) {
	 Attribute atributo = (Attribute) atributos.next();
	   if(atributo.getKey().equals(key))
		  exist= true;
	      rta= atributo.getValue();
	   }
	 }	
	return rta;
}

private boolean typeA(Element elemento) {
	
	return elemento.getName().equals(HTMLElementName.A);
}

private boolean isLink(Element elemento) {
	
	boolean typeA= typeA(elemento);
		
	boolean hrefExist= !"".equals(getAttValue(elemento, "href"));
	return(typeA && hrefExist);
}

public void parse(UrlWrapper urlElement,Iterator<Element> elementos, Element elemento, Iterator<TemplateStructure> templateIter, TemplateStructure templateNode, boolean validarHijos) throws Exception {
	
	
	    UrlWrapper newElement = null;	
	    boolean nextMatch=false;
	    if(urlElement!=null){
		//VEr funcion de matching
				if(matching(elemento, templateNode,validarHijos)){
					 
					if(validarHijos){
						nextMatch= matchingElSiguiente(templateNode,elemento);
						//cambiar las llamadas
					}
					
					newElement = new UrlWrapper();
					
					newElement.setParentUrl(urlElement);
						if(isLink(elemento)){					
							newElement.setLink(getAttValue(elemento, "href"));							
							
						}else{
							
//							newElement.setText(templateNode.getTitle());
						}
						//Es item
						if(elemento.getChildElements().size()==0 && templateNode.isItem()){						
								
							if(elementos.hasNext()){
		//						urlElement.addChild(newElement);
		//						newElement.getParentUrl().setValid();
								parse(newElement,elementos, elementos.next(),templateIter,templateIter.next(),nextMatch);					
								}
							else{
		//						newElement.getParentUrl().setValid();
								urlElement.addChild(newElement);					
								
						    }			
							newElement.setValid();
						}else{
							
							//Es seccion 	
							if(!templateNode.isItem()){
								//PAgina con hijos
								if(elemento.getChildElements().size()>0){
								
									Iterator<TemplateStructure> templateChild=  templateNode.getStrcture().iterator();
									Iterator<Element> subElement = elemento.getChildElements().iterator();
									//Agrego a estructura y y llamo con ambos hijos
									
									urlElement.addChild(newElement);
									if(!templateChild.hasNext()){
										//Es una seccion en template pero sin hijos definidos. tengo que generar la estructura pero 
										System.out.println("SeccionVAcia");
										parse(newElement,elementos, elementos.next(),templateIter,templateIter.next(),true);
									}else{
										parse(newElement,subElement, subElement.next(),templateChild,templateChild.next(),nextMatch);
										if(newElement.isVAlid()){
											newElement.setValid();
										}
									}
		//							luego llamo con siguientes tambien
									if(elementos.hasNext()){
										if(newElement.isVAlid()){
											newElement.setValid();
											if(templateIter.hasNext())
												parse(urlElement,elementos,elementos.next(),templateIter, templateIter.next(),nextMatch);
										}else{
											
											parse(urlElement,elementos,elementos.next(),templateChild, templateNode,nextMatch);
										}
										if(urlElement.isVAlid()){
											urlElement.setValid();
										}		
									}
									
										
								}else{// tag sin hijos
									
									Iterator<TemplateStructure> templateChild=  templateNode.getStrcture().iterator();
									
									urlElement.addChild(newElement);
									if(templateChild.hasNext()){
										parse(newElement,elementos, elementos.next(),templateChild,templateChild.next(),nextMatch);
									}else{
										//Section vacia en el template. Tengo que validar todo lo que haya dentro hasta 
										//que haga maching con el siguientenodo del template
										
										parse(newElement,elementos, elementos.next(),templateIter,templateIter.next(),true);
									}
									if(newElement.isVAlid()){
										newElement.setValid();
									}
									if(elementos.hasNext()){
										if(newElement.isVAlid()){
											if(templateChild.hasNext()){
												parse(newElement,elementos,elementos.next(),templateChild, templateChild.next(),nextMatch);
												if(newElement.isVAlid()){
													newElement.setValid();
												}else{
													newElement.setValid(false);
												}
												
											}else{
												if(templateIter.hasNext()){
													parse(urlElement,elementos,elementos.next(),templateIter, templateIter.next(),nextMatch);
													if(urlElement.isVAlid()){
														urlElement.setValid();
													}else{
														newElement.setValid(false);
													}
												}
												
											}
											
										}else{
											parse(newElement,elementos,elementos.next(),templateChild, templateNode,nextMatch);
										
											if(newElement.isVAlid()){
												newElement.setValid();
											}
										}
											
									}
									
								
								}
							
								
							}							
							else
							{//Es un item 
								if(elementos.hasNext()){
									//Iterator<TemplateStructure> templateChild=  templateNode.getStrcture().iterator();
									Iterator<Element> subElement = elemento.getChildElements().iterator();
									//Agrego a estructura y y llamo con hijo de la pagina
									 
		//							 urlElement.addChild(newElement);
									 parse(newElement,subElement, subElement.next(),templateIter,templateNode,nextMatch);
									 // avanzo en ambas estructuras
									
									 if(newElement.isVAlid()){
										 newElement.setValid();
										}
									
									}
								else{
									newElement.setValid();
									urlElement.addChild(newElement);		
									
							    }
							
							}
									
						}			
						
				}else{// No hay matching
					
					 //tiene hijos el tag
					 if(elemento.getChildElements().size()>0){
							
						 Iterator<Element> subElement = elemento.getChildElements().iterator();
						 parse(urlElement,subElement, subElement.next(),templateIter,templateNode,false);
						 if(elementos.hasNext()){
								if(urlElement !=null && urlElement.isVAlid()){
									if(templateIter.hasNext())
									parse(urlElement,elementos,elementos.next(),templateIter, templateIter.next(),nextMatch);
								}else{
									parse(urlElement,elementos,elementos.next(),templateIter, templateNode,nextMatch);
								}
									
							}
						 
						 
						 
					 }else{
						 if(elementos.hasNext()){
		//						
							 parse(urlElement,elementos, elementos.next(),templateIter,templateNode,nextMatch);
						 }
					 }
					 
					 if(urlElement.isVAlid()){
							urlElement.setValid();
					 }else{
						 if( urlElement.getParentUrl()!=null){
						  urlElement.getParentUrl().setValid(false);
						  
						 }else{
							 urlElement.setValid(false);
							
						 }
					 }
				}
		
	    }
	
		 		
}

private boolean matchingElSiguiente(TemplateStructure templateNode,
		Element elemento) {
	boolean rta=false;
	if(isLink(elemento)){
		
		rta=  elemento.getRenderer().toString().equals(templateNode.getTitle());
	}else{
		
		if(elemento.getChildElements().isEmpty()){
			rta=  elemento.getRenderer().toString().equals(templateNode.getTitle());
		}else
		{
			String todo = elemento.getTextExtractor().toString();
			String resto="";
			List <Element> hijos= elemento.getChildElements();
			for(int i=0;i<hijos.size();i++){
				if(!hijos.get(i).getTextExtractor().toString().isEmpty()){
					resto+=(hijos.get(i).getTextExtractor().toString());
					if(i<hijos.size()-1)
					resto+=" ";
				}
				
			}
			String resultado="";
			if(todo.length()> resto.length()){
			  resultado=todo.substring(0, todo.length()-resto.length()-1);
			}
			rta=resultado.equals(templateNode.getTitle());
	
		}
		
	}
	
	return rta;
}



private boolean matching(Element elemento, TemplateStructure templateNode,boolean validarHijos) {
	boolean rta=false;
	
	if(!validarHijos){
		  
			if(isLink(elemento)){
				
				rta=  elemento.getRenderer().toString().equals(templateNode.getTitle());
			}else{
				
				if(elemento.getChildElements().isEmpty()){
					rta=  elemento.getRenderer().toString().equals(templateNode.getTitle());
				}else
				{
					String todo = elemento.getTextExtractor().toString();
					String resto="";
					List <Element> hijos= elemento.getChildElements();
					for(int i=0;i<hijos.size();i++){
						if(!hijos.get(i).getTextExtractor().toString().isEmpty()){
							resto+=(hijos.get(i).getTextExtractor().toString());
							if(i<hijos.size()-1)
							resto+=" ";
						}
						
					}
					String resultado="";
					if(todo.length()> resto.length()){
					  resultado=todo.substring(0, todo.length()-resto.length()-1);
					}
					rta=resultado.equals(templateNode.getTitle());
				  
				  // (elemento.getBegin(), elemento.getChildElements().get(0).getBegin()).toString();
				}
	//			elemento.subSequence(elemento.getBegin(),  elemento.getContent().getFirstStartTag().getBegin());
		       }
		}else{
			rta=validarHijos;
		}
		return rta;
}



public StringBuilder getCompleteText() {
	
	int section=0;
	StringBuilder text= new StringBuilder();
	text.append("Documento de arquitectura aplanado"+ "\n");
	Iterator<UrlWrapper> urlsIter= structureWrapped.getChildUrls().iterator();
	
	for (; urlsIter.hasNext();) {
		section++;
		text.append("\n\n"+ "Seccion "+ section+"\n\n");
		UrlWrapper url = (UrlWrapper) urlsIter.next();
		text.append(url.getText());				
	}
	
	return text;
}

}
